package com.qd.system.importexcel.person;

import com.alibaba.excel.read.metadata.holder.ReadRowHolder;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cdqidi.dto.BatchResult;
import com.cdqidi.excel.domain.ExcelRowMsg;
import com.cdqidi.excel.domain.ImportErr;
import com.cdqidi.excel.util.ExcelUtil;
import com.cdqidi.exception.ExcelOperatorException;
import com.cdqidi.util.ValidatorUtil;
import com.cdqidi.valid.group.DefaultGroup;
import com.qd.common.sys.domain.entity.login.LoginUserDTO;
import com.qd.common.sys.domain.entity.person.PersonDTO;
import com.qd.common.sys.enums.BaseTableEnum;
import com.qd.common.sys.util.SecurityUtils;
import com.qd.system.model.LoginUser;
import com.qd.system.model.OrgPerson;
import com.qd.system.model.Person;
import com.qd.system.service.dict.ExcelValidateUtil;
import com.qd.system.service.dict.MbUtil;
import com.qd.system.service.person.PersonHoldService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.validation.ConstraintViolationException;
import java.time.LocalDateTime;
import java.util.*;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

/**
 * @author sjk
 */
@Slf4j
public class PersonImportHandler {
    private final PersonHoldService personHoldService;
    private final ValidatorUtil validatorUtil;
    private final List<ImportErr> errorList;
    private final LocalDateTime now = LocalDateTime.now();
    private final ExcelValidateUtil excelValidateUtil;
    private final String defaultPassword;
    private final String encoderDefaultPassword;

    public PersonImportHandler(PersonHoldService personHoldService, ValidatorUtil validatorUtil, List<ImportErr> errorList, String defaultPassword, String encoderDefaultPassword) {
        this.personHoldService = personHoldService;
        this.validatorUtil = validatorUtil;
        this.errorList = errorList;
        this.excelValidateUtil = new ExcelValidateUtil();
        this.defaultPassword = defaultPassword;
        this.encoderDefaultPassword = encoderDefaultPassword;
    }


    public void process(final List<ExcelRowMsg<PersonDTO>> personList, final List<BatchResult> results) {
        final int size = personList.size();
        final List<PersonDTO> personDTOList = personList.stream().map(ExcelRowMsg::getRowData).collect(toList());
        final List<String> allPersonIds = personDTOList.stream().map(PersonDTO::getPersonId).filter(StringUtils::hasLength).collect(toList());
        final List<String> existsPersonIds = existsPersonList(allPersonIds);
        final List<LoginUserDTO> existsLoginUserByIdList = existsLoginUserByIdList(allPersonIds);
        final List<LoginUserDTO> existsLoginUserByAccountList = existsLoginUserByAccountList(personDTOList);
        final List<String> existsOrgIds = existsOrgIds(personDTOList);
        try {
            List<String> errors;
            for (ExcelRowMsg<PersonDTO> excelRowMsg : personList) {
                errors = new ArrayList<>();
                final PersonDTO personDto = excelRowMsg.getRowData();
                final ReadRowHolder readRowHolder = excelRowMsg.getReadRowHolder();
                checkBaseColumn(personDto, existsOrgIds, errors);
                importValidate(personDto, errors);
                checkLoginUser(personDto, existsLoginUserByIdList, existsLoginUserByAccountList, errors);
                if (!errors.isEmpty()) {
                    errorList.add(ExcelUtil.buildError(readRowHolder, errors));
                }
            }
            //准备写入数据库了
            if (errorList.isEmpty()) {
                //人员信息
                final List<Person> addPersonList = new ArrayList<>(size);
                final List<Person> updatePersonList = new ArrayList<>(size);
                doProcessPerson(existsPersonIds, personDTOList, addPersonList, updatePersonList);
                BatchResult.buildBatchResult(results, addPersonList.size(), updatePersonList.size());
                addPersonList.clear();
                updatePersonList.clear();

                //登录信息
                final List<LoginUser> addLoginUserList = new ArrayList<>(size);
                final List<LoginUser> updateLoginUserList = new ArrayList<>(size);
                doProcessLoginUser(personDTOList, existsLoginUserByIdList, existsLoginUserByAccountList, addLoginUserList, updateLoginUserList);
                addLoginUserList.clear();
                updateLoginUserList.clear();

                //人员和机构关系信息
                doProcessOrgPerson(allPersonIds, personDTOList);
            } else {
                throw new ExcelOperatorException(errorList);
            }
        } finally {
            personDTOList.clear();
            existsPersonIds.clear();
            allPersonIds.clear();
            existsLoginUserByIdList.clear();
            existsLoginUserByAccountList.clear();
            existsOrgIds.clear();
        }
    }

    private void doProcessOrgPerson(final List<String> allPersonIds, final List<PersonDTO> personDTOList) {
        final List<String> orgIds = personDTOList.stream().map(PersonDTO::getOrgId).collect(toList());
        final List<String> existsOrgPersonList = existsOrgPersonList(orgIds, allPersonIds);
        final List<OrgPerson> orgPersonList = personDTOList.stream().filter(personDto -> !MbUtil.isExistInCollection(existsOrgPersonList, personDto.getPersonId())).map(personDto -> {
            OrgPerson orgPerson = new OrgPerson();
            orgPerson.setPersonId(personDto.getPersonId());
            orgPerson.setOrgId(personDto.getOrgId());
            orgPerson.setAddTime(personDto.getAddTime());
            return orgPerson;
        }).collect(toList());
        personHoldService.saveOrgPersons(orgPersonList);
        personHoldService.getOrgPersonService().clearCaches(orgPersonList);
        orgIds.clear();
        existsOrgPersonList.clear();
        orgPersonList.clear();
    }

    private void doProcessPerson(final List<String> existsPersonIds, final List<PersonDTO> personDTOList, final List<Person> addPersonList, final List<Person> updatePersonList) {
        for (PersonDTO personDto : personDTOList) {
            boolean isAdd = true;
            if (MbUtil.isExistInCollection(existsPersonIds, personDto.getPersonId())) {
                //update
                personDto.setPassword(null);
                personDto.setAddTime(null);
                personDto.setUpdateTime(now);
                isAdd = false;
            } else {
                String password = personDto.getPassword();
                //add
                if (!StringUtils.hasLength(password)) {
                    password = encoderDefaultPassword;
                } else {
                    if (StringUtils.hasLength(defaultPassword) && password.equals(defaultPassword)) {
                        password = encoderDefaultPassword;
                    } else {
                        password = SecurityUtils.encryptPassword(password);
                    }
                }
                if (!StringUtils.hasLength(personDto.getPersonId())) {
                    personDto.setPersonId(IdWorker.getIdStr());
                }
                personDto.setPassword(password);
                personDto.setAddTime(now);
                personDto.setUpdateTime(null);
            }
            final Person person = personHoldService.getPersonService().dtoToModel(personDto);
            if (isAdd) {
                addPersonList.add(person);
            } else {
                //清理缓存
                personHoldService.getPersonService().clearCache(person);
                updatePersonList.add(person);
            }
        }
        personHoldService.savePersons(addPersonList, updatePersonList);
    }

    private void doProcessLoginUser(final List<PersonDTO> personDTOList, final List<LoginUserDTO> existsLoginUserByIdList, final List<LoginUserDTO> existsLoginUserByAccountList, final List<LoginUser> addLoginUserList, final List<LoginUser> updateLoginUserList) {
        final Set<String> allSet = new HashSet<>();
        if (!existsLoginUserByIdList.isEmpty()) {
            final Set<String> existsLoginUserByIdSet = existsLoginUserByIdList.stream().map(LoginUserDTO::getPersonId).collect(toSet());
            if (!existsLoginUserByIdSet.isEmpty()) {
                allSet.addAll(existsLoginUserByIdSet);
            }
        }
        if (!existsLoginUserByAccountList.isEmpty()) {
            final Set<String> existsLoginUserByAccountSet = existsLoginUserByAccountList.stream().map(LoginUserDTO::getPersonId).collect(toSet());
            if (!existsLoginUserByAccountSet.isEmpty()) {
                allSet.addAll(existsLoginUserByAccountSet);
            }
        }
        for (PersonDTO personDto : personDTOList) {
            final LoginUser loginUser = personHoldService.buildLoginUser(personDto);
            boolean isAdd = true;
            if (MbUtil.isExistInCollection(allSet, personDto.getPersonId())) {
                //update
                //清理缓存
                personHoldService.getLoginUserService().clearCache(loginUser);
                loginUser.setPassword(null);
                isAdd = false;
            }
            if (isAdd) {
                addLoginUserList.add(loginUser);
            } else {
                updateLoginUserList.add(loginUser);
            }
        }
        allSet.clear();
        personHoldService.saveLoginUsers(addLoginUserList, updateLoginUserList);
    }

    /**
     * 码表数据验证
     *
     * @param personDto    行数据
     * @param existsOrgIds 存在的机构ID
     * @param errors       异常信息
     */
    private void checkBaseColumn(final PersonDTO personDto, final List<String> existsOrgIds, final List<String> errors) {
        //验证信息，自己编写
        final Integer orgStatus = MbUtil.isStatusValidate(errors, personDto.getUserStatusDisplay());
        if (null != orgStatus) {
            personDto.setUserStatus(orgStatus);
        }
        if(StringUtils.hasLength(personDto.getSexDisplay())){
            final String sex = excelValidateUtil.isValidBase(personDto.getSexDisplay(), BaseTableEnum.SEX, errors);
            if (null != sex) {
                personDto.setSex(sex);
            }
        }
        personHoldService.getOrgService().isValidOrg(existsOrgIds, personDto.getOrgId(), errors);
    }

    /**
     * 导入实体验证
     *
     * @param personDto 导入实体
     * @param errors    异常信息存储
     */
    private void importValidate(final PersonDTO personDto, final List<String> errors) {
        if (errors.isEmpty()) {
            try {
                validatorUtil.checkValid(personDto, DefaultGroup.class);
            } catch (ConstraintViolationException e) {
                errors.addAll(validatorUtil.getValidationError(e));
            }
        }
    }

    /**
     * 查询存在的机构和人员关系
     *
     * @param orgIdsList    机构ID
     * @param personIdsList 人员ID
     * @return 存在的人员ID
     */
    private List<String> existsOrgPersonList(final List<String> orgIdsList, final List<String> personIdsList) {
        if (personIdsList.isEmpty()) {
            return Collections.emptyList();
        }
        return personHoldService.getOrgPersonService().getBaseMapper().getOrgPersons(orgIdsList, personIdsList);
    }

    /**
     * 按人员ID查询已经存在的登录用户
     *
     * @param personIdsList 人员ID
     * @return 存在的登录信息
     */
    private List<LoginUserDTO> existsLoginUserByIdList(final List<String> personIdsList) {
        if (null == personIdsList || personIdsList.isEmpty()) {
            return Collections.emptyList();
        }
        return personHoldService.getLoginUserService().getBaseMapper().getIds(personIdsList);
    }

    /**
     * 按人员账号查询已经存在的登录用户
     *
     * @param personDTOList 人员信息
     * @return 存在的登录信息
     */
    private List<LoginUserDTO> existsLoginUserByAccountList(final List<PersonDTO> personDTOList) {
        if (null == personDTOList || personDTOList.isEmpty()) {
            return Collections.emptyList();
        }
        final List<String> accountList = personDTOList.stream()
                .map(PersonDTO::getAccount)
                .filter(StringUtils::hasLength).collect(toList());
        if (accountList.isEmpty()) {
            return Collections.emptyList();
        }
        final List<LoginUserDTO> list = personHoldService.getLoginUserService().getBaseMapper().getUserByAccount(accountList);
        accountList.clear();
        return list;
    }

    /**
     * 查询已经存在的用户ID
     *
     * @param personIdsList 用户ID
     * @return 存在的用户信息集合
     */
    private List<String> existsPersonList(final List<String> personIdsList) {
        if (null == personIdsList || personIdsList.isEmpty()) {
            return Collections.emptyList();
        }
        return personHoldService.getPersonService().getBaseMapper().getIds(personIdsList);
    }

    /**
     * 在数据库中查询机构ID存在的集合
     *
     * @param personDTOList 人员列表
     * @return 存在的机构ID
     */
    private List<String> existsOrgIds(final List<PersonDTO> personDTOList) {
        if (null == personDTOList || personDTOList.isEmpty()) {
            return Collections.emptyList();
        }
        final List<String> orgIds = personDTOList.stream().map(PersonDTO::getOrgId).filter(StringUtils::hasLength).collect(toList());
        if (orgIds.isEmpty()) {
            return Collections.emptyList();
        }
        final List<String> existsOrgIds = personHoldService.getOrgService().getBaseMapper().getIds(orgIds);
        orgIds.clear();
        return existsOrgIds;
    }

    /**
     * 登录用户验证
     *
     * @param personDto                    行信息
     * @param existsLoginUserByIdList      按ID查询的登录用户
     * @param existsLoginUserByAccountList 按账号查询的登录用户
     * @param errors                       异常信息
     */
    private void checkLoginUser(final PersonDTO personDto,
                                final List<LoginUserDTO> existsLoginUserByIdList,
                                final List<LoginUserDTO> existsLoginUserByAccountList,
                                final List<String> errors) {
        if (errors.isEmpty()) {
            String msg;
            final List<LoginUserDTO> existsAccount = existsLoginUserByAccountList.stream().filter(u -> {
                boolean b = false;
                if (u.getAccount().equals(personDto.getAccount())) {
                    if (StringUtils.hasLength(personDto.getPersonId())) {
                        if (!u.getPersonId().equals(personDto.getPersonId())) {
                            b = true;
                        }
                    } else {
                        b = true;
                    }
                }
                return b;
            }).collect(toList());
            if (!existsAccount.isEmpty()) {
                msg = "当前账号已经分配给[" + existsAccount.get(0).getPersonName() + "]";
                errors.add(msg);
            }

            final List<LoginUserDTO> existsPersonIds = existsLoginUserByIdList.stream().filter(u -> {
                boolean b = false;
                if (StringUtils.hasLength(personDto.getPersonId())) {
                    if (u.getPersonId().equals(personDto.getPersonId()) && !u.getAccount().equals(personDto.getAccount())) {
                        b = true;
                    }
                }
                return b;
            }).collect(toList());
            if (!existsPersonIds.isEmpty()) {
                msg = "当前用户ID已经分配给账号[" + existsPersonIds.get(0).getAccount() + "]";
                errors.add(msg);
            }
        }

    }
}
